<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<link rel="stylesheet" type="text/css" href="../css/common.css" media="all" />
<link rel="stylesheet" type="text/css" href="../css/article.css" media="all" />
</head>
<body>
<div id="w3h_body">
  <div class="body_content">
    <!-- toc begin -->
    <h1 class="title">SD9019: 插入空白页面 IFRAME 元素时 Chrome Safari Opera 浏览器中会触发 load 事件</h1>
    <ul class="toc">
      <li><a href="#standard_reference">标准参考</a> <span>•</span></li>
      <li><a href="#description">问题描述</a> <span>•</span></li>
      <li><a href="#influence">造成的影响</a> <span>•</span></li>
      <li><a href="#impacted_browsers">受影响的浏览器</a> <span>•</span></li>
      <li><a href="#analysis_of_issues">问题分析</a> <span>•</span></li>
      <li><a href="#solutions">解决方案</a> <span>•</span></li>
      <li><a href="#see_also">参见</a></li>
    </ul>
    <!-- toc end -->
    <div id="w3h_content">
      <!-- content begin -->
      <address class="author">作者：钱宝坤</address>
      <h2 id="standard_reference">标准参考</h2>
      <p>关于 HTML 4.01 规范中 onload 内在事件说明：<a href="http://www.w3.org/TR/html401/interact/scripts.html#adef-onload">http://www.w3.org/TR/html401/interact/scripts.html#adef-onload</a> </p>

      <p>关于 DOM Level2 Events 规范中 load 事件说明：<a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-htmlevents">http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-htmlevents</a></p>
      <h2 id="description">问题描述</h2>
      <p>插入空白页面 IFRAME 元素时 Chrome Safari Opera 浏览器中会触发 load 事件。</p>

      <h2 id="influence">造成的影响</h2>
      <p>Chrome Safari Opera 浏览器中，在插入 &quot;src&quot; 属性未设置或设置为空字符串的 IFRAME 元素后立即修改其 &quot;src&quot; 属性值，会导致 IFRAME 标记绑定的 load 事件再被触发。</p>
      <p>触发 IE 和 Firefox 浏览器中 IFRAME 标记 &quot;src&quot; 属性值的历史记录问题。</p>

      <h2 id="impacted_browsers">受影响的浏览器</h2>
      <table class="list">
        <tr>
          <th>所有浏览器</th>
          <td></td>
        </tr>
      </table>

      <h2 id="analysis_of_issues">问题分析</h2>
      <p>根据规范说明，load 事件可以作用于 FRAMESET 标记中，因此同样也可以作用于 IFRAME 标记上。它表示着 IFRAME 内页面已经完全加载完毕 IFRAME 标记处于可用状态。 </p>
      <p>如果创建的 IFRAME 标记没有设定 &quot;src&quot; 属性和值，那么他的 load 事件是否会被触发呢？</p>
      <p>分析以下代码：</p>
<pre>
function iframeLoad(){
  alert(&quot;IFRAME 标记的 onload 事件触发&quot;);
}
function addEvent(eventName,element,fn){
  if (element.attachEvent) element.attachEvent(&quot;on&quot;+eventName,fn);
  else element.addEventListener(eventName,fn,false);
}
window.onload = function (){
  var iframe = document.createElement('iframe');
  addEvent(&quot;load&quot;,iframe,iframeLoad);
  document.body.appendChild(iframe);
  iframe.src=&quot;iframe.html&quot;;


}
</pre>
      <p>代码中动态创建了 IFRAME 标记，为其监听 load 事件，此时还没有显示性的设定他的 &quot;src&quot; 值就将他加入文档树中。此后立即为 IFRAME 设置 &quot;src&quot; 属性，指向具体的 URL 。</p>
      <p>观察各浏览器中运行效果：</p>
      <table class="compare">
        <tr>
        <th>&nbsp;</th>
        <th>IE Firefox</th>
        <th>Chrome Safari Opera</th>
        </tr>
        <tr>
        <th>IFRAME onload 事件触发次数</th>
        <td>1次</td>
        <td class="hl_4">2次</td>
        </tr>
      </table>
      <p>看到 Chrome Safari Opera 中 onload 事件被触发两次。</p>
      <p>为了搞清这个问题，我们需要知道没有设置 src 属性的 IFRAME 被添加到 DOM 树中后，其默认的 &quot;src&quot; 值是什么。继续分析以下代码：</p>
<pre>
function iframeLoad(iframe){
  document.body.appendChild(document.createTextNode(&quot;IFRAME URL : &quot;+iframe.location.href));
}
function addEvent(eventName,element,fn){
  if (element.attachEvent) element.attachEvent(&quot;on&quot;+eventName,fn);
  else element.addEventListener(eventName,fn,false);
}
window.onload = function (){
  var iframe = document.createElement('iframe');
  addEvent(&quot;load&quot;,iframe,function (){iframeLoad(iframe.contentWindow)});
  document.body.appendChild(iframe);
}
</pre>
      <table class="compare">
        <tr>
          <th>&nbsp;</th>
          <th>所有浏览器</th>
        </tr>
        <tr>
          <th>URL</th>
          <td>about:blank</td>
        </tr>
      </table>
      <p>这里将空 src 值 IFRAME 标记内调用页面的 URL 打印出来，可以发现所有浏览器中处理一致，均为 &quot;about:blank&quot; 。这个页面是个空 HTML 文档页，所有浏览器均内置提供。这个空页面被加载完成后同样会触发 IFRAME 标记的 onload 事件。</p>
      <p>由此可以推测，是否由于 Chrome Safari Opera 浏览器中当 IFRAME 标记被插入文档树后，载入&quot;about:blank&quot; 页面速度非常快立即就触发了 load 事件，以至于还没来得及执行为他指定新 URL 的语句；当新 URL 指定后，当前页面加载完成时又触发了一次 IFRAME 标记的 load 事件，这样总共就触发了两次。</p>
      <p>而其他浏览器则是载入 &quot;about:blank&quot;  页花费时间加多，页面并未完全加载时其 URL 已经被改变，因此相对  Chrome Safari Opera 浏览器第一次 load 事件并没有触发，只有等新 URL 中的页面加载完毕后 load 事件才被触发，这样总共只触发了一次。 </p>
      <p>为了证明以上猜测，将一段延时执行的代码加入其中：</p>
<pre>
function iframeLoad(iframe){
  var src = (iframe.src)?iframe.src:iframe.contentWindow.location.href;
  document.body.appendChild(document.createElement(&quot;br&quot;));
  document.body.appendChild(document.createTextNode(&quot;IFAME 标记 src 值为 &quot;+ src + &quot; 的 onload 事件触发&quot;));
}
function addEvent(eventName,element,fn){
  if (element.attachEvent) element.attachEvent(&quot;on&quot;+eventName,fn);
  else element.addEventListener(eventName,fn,false);
}
window.onload = function (){
  var iframe = document.createElement('iframe');
  addEvent(&quot;load&quot;,iframe,function (){iframeLoad(iframe)});
  <span class="hl_4">iframe.src= &quot;about:blank&quot;;</span>
  document.body.appendChild(iframe);
  <span class="hl_4">setTimeout(function (){iframe.src=&quot;iframe.html&quot;;},300)</span>
}
</pre>
      <p>代码中，在 IFRAME 节点被添加到文档树树后，延时 300 毫秒再改变它的 src 属性，以便给其他浏览器充足的事件将 &quot;about:blank&quot; 页面加载完成。</p>
      <p>此时各浏览器执行结果一致：</p>
      <table class="compare">
        <tr>
          <th>&nbsp;</th>
          <th>所有浏览器</th>
        </tr>
        <tr>
          <th>IFRAME onload 事件触发次数</th>
          <td>
            'about:blank' 页面1次<br />
            'iframe.html' 页面1次<br />
            共2次
          </td>
        </tr>
      </table>
      <p>这个结果证明了之前的猜想：Chrome Safari Opera 浏览器执行速度比想象中的要快的多，导致好像“多”触发了一次 load 事件，实际上所有浏览器均会为 IFRAME 标记内每个页面触发他的 load 事件，前提是让这些页面有足够的事件加载完成。</p>
      <p><em>【注】：使用appendChild 方法将没有设置 src 属性的 IFRAME 插入文档树和使用 innnerHTML 方式将没有写 src 属性的 IFRAME 标记字符串插入文档树时，均会存在以上分析的问题。</em></p>
      <p>&nbsp;</p>
      <p>最后来看，上面的代码中在插入节点之前为 IFRAME 标记显式性的设置了 src 属性值为 &quot;about:blank&quot; 的页面，这是为了避免 Firefox 中 URL 缓存的 Bug。 </p>
      <p>在 IE 和 Firefox 中，如果 IFRAME 没有显示性的设置 &quot;src&quot; ，如果该页面第一次被打开，则实际页面的 src 值为 &quot;about:blank&quot;；否则将为 IFRME 标记中最后一个被设定的 &quot;src&quot; 值。</p>
      <p>分析以下代码（没有明确定义 src 属性值）：</p>
<pre>
window.onload = function (){
  var iframe = document.createElement('iframe');
  addEvent(&quot;load&quot;,iframe,function (){iframeLoad(iframe)});
  document.body.appendChild(iframe);
  setTimeout(function (){iframe.src=&quot;iframe.html&quot;;},300)
}
</pre>

      <p>注意代码 &quot;src&quot; 值最初未定义，最终被修改为 &quot;ifarme.html&quot;，观察在各浏览器中运行结果：</p>
      <table class="compare">
        <tr>
          <th>&nbsp;</th>
          <th>Chrome Safari Opera</th>
          <th> IE Firefox</th>
        </tr>
        <tr>
          <th>首次显示</th>
          <td colspan="2">
            'about:blank' 页 onload 事件触发<br />
            'iframe.html' 页 onload 事件触发
          </td>
        </tr>
        <tr>
          <th>F5 普通刷新</th>
          <td>
            'about:blank' 页 onload 事件触发<br />
            'iframe.html' 页 onload 事件触发
          </td>
          <td class="hl_1">
            'iframe.html' 页 onload 事件触发<br />
            'iframe.html' 页 onload 事件触发</td>
        </tr>
        <tr>
          <th>Ctrl + F5 强制刷新</th>
          <td colspan="2">
            'about:blank' 页 onload 事件触发<br />
            'iframe.html' 页 onload 事件触发
          </td>
        </tr>
      </table>
      <p>可见，IE 和 Firefox 浏览器中出现了 IFRAME 元素的 &quot;src&quot; 属性最后指向的记忆问题，普通刷新页面后，没有明确设置 &quot;src&quot; 属性的 IFRAME 标记将默认使用上一条历史记录中的 &quot;src&quot; 值。 </p>

      <h2 id="solutions">解决方案</h2>
      <p>为 IFRAME 标签的 src 属性指定具体 URL 后再将节点插入 DOM 树中 。</p>

      <h2 id="see_also">参见</h2>
      <h3>知识库</h3>
      <ul class="see_also">
        <li><a href="#">...</a></li>
      </ul>
      <h3>相关问题</h3>
      <ul class="see_also">
        <li><a href="#">...</a></li>
      </ul>

      <div class="appendix">
        <h2>测试环境</h2>
        <table class="list">
          <tr>
            <th>操作系统版本:</th>
            <td>Windows 7 Ultimate build 7600</td>
          </tr>
          <tr>
            <th>浏览器版本:</th>
            <td>
              IE6<br />
              IE7<br />
              IE8<br />
              Firefox 3.6<br />
              Chrome 4.0.302.3 dev<br />
              Safari 4.0.4<br />
                            Opera 10.60
            </td>
          </tr>
          <tr>
            <th>测试页面:</th>
            <td>
              <a href="../../tests/SD9019/appendChild_iframe_null_src_onload.html">appendChild_iframe_null_src_onload.html</a><br />
              <a href="../../tests/SD9019/appendChild_iframe_null_src_url.html">appendChild_iframe_null_src_url.html</a><br />
              <a href="../../tests/SD9019/appendChild_iframe_null_src_setTimeout_set_src.html">appendChild_iframe_null_src_setTimeout_set_src.html</a><br />
              <a href="../../tests/SD9019/innerHTML_iframe_null_src_onload.html">innerHTML_iframe_null_src_onload.html</a><br />
              <a href="../../tests/SD9019/innerHTML_iframe_null_src_url.html">innerHTML_iframe_null_src_url.html</a><br />
              <a href="../../tests/SD9019/innerHTML_iframe_src_setTimeout_set_src.html">innerHTML_iframe_src_setTimeout_set_src.html</a><br />
              <a href="../../tests/SD9019/ie_and_firefox__url_history_bug.html">ie_and_firefox__url_history_bug.html</a>

            </td>
          </tr>
          <tr>
            <th>本文更新时间:</th>
            <td>2010-07-28</td>
          </tr>
        </table>
        <h2>关键字</h2>  
        <!-- keywords begin -->
        <p>IFRAME URL src load 加载 历史记录 缓存</p>
        <!-- keywords end -->
      </div>
      <!-- content end -->
    </div>
  </div>
</div>
</body>
</html>
